import uiautomator2 as u2
import time
import xml.etree.ElementTree as ET
from xhs import config
import traceback
from xhs.my_log import log
from xhs import db_queue
import winsound
import threading

# 跟踪设备连接状态，确保同一设备同时只有一个连接
_device_connections = {}
_device_locks = {}

def get_device_connection(device_id):
    """
    获取设备连接，确保同一设备同时只有一个连接
    """
    if device_id not in _device_locks:
        _device_locks[device_id] = threading.Lock()

    with _device_locks[device_id]:
        if device_id not in _device_connections:
            # 连接不存在，创建新连接
            try:
                d = u2.connect_usb(device_id)
                _device_connections[device_id] = d
                return d
            except Exception as e:
                log(f"[{device_id}] 连接设备失败: {str(e)}")
                return None
        else:
            # 检查现有连接是否有效
            d = _device_connections[device_id]
            try:
                # 尝试执行简单命令检查连接
                d.device_info
                return d
            except Exception as e:
                log(f"[{device_id}] 连接已失效，重新连接: {str(e)}")
                try:
                    # 尝试重新连接
                    d = u2.connect_usb(device_id)
                    _device_connections[device_id] = d
                    return d
                except Exception as e2:
                    log(f"[{device_id}] 重新连接失败: {str(e2)}")
                    return None


def release_device_connection(device_id):
    """
    释放设备连接
    """
    with _device_locks.get(device_id, threading.Lock()):
        if device_id in _device_connections:
            try:
                # 关闭连接
                _device_connections[device_id].disconnect()
                del _device_connections[device_id]
                log(f"[{device_id}] 已释放设备连接")
            except Exception as e:
                log(f"[{device_id}] 释放设备连接异常: {str(e)}")


# 全局任务状态跟踪
_task_status = {}
_task_status_lock = threading.Lock()


def set_task_status(device_id, status):
    """
    设置任务状态
    :param device_id: 设备ID
    :param status: 状态 (idle, running)
    """
    with _task_status_lock:
        _task_status[device_id] = status
        log(f"[{device_id}] 任务状态更新为: {status}")


def get_task_status(device_id):
    """
    获取任务状态
    :param device_id: 设备ID
    :return: 状态 (idle, running) 默认为 idle
    """
    with _task_status_lock:
        return _task_status.get(device_id, "idle")


def backTable(device_name, device_id, red_id, message, max_back=10):
    """
    连续按返回键，直到返回桌面或达到最大次数
    :param d: uiautomator2 设备对象
    :param max_back: 最大返回次数（默认10次）
    """
    desktop_pkgs = [
        "com.android.launcher",  # 原生桌面
        "com.miui.home",  # 小米桌面
        "com.huawei.android.launcher",  # 华为桌面
        "com.oppo.launcher",  # OPPO桌面
        "com.vivo.launcher"  # vivo桌面
    ]

    d = u2.connect_usb(device_id)
    for _ in range(max_back):
        try:
            current_pkg = d.app_current().get('package', "")
            if any(pkg in current_pkg for pkg in desktop_pkgs):
                log(f"[{device_name}][{device_id}][{red_id}]已返回桌面, 原因: {message}")
                return True  # 成功退回桌面

            d.press("back")
            time.sleep(1)  # 每次返回后稍作等待
        except Exception as e:
            log(f"[{device_name}][{device_id}][{red_id}]退回桌面失败, 原因: {message},异常: {str(e)}")
            return False

    log(f"[{device_name}][{device_id}][{red_id}]退回桌面超时（尝试 {max_back} 次）")
    return False

def back(d, count, device_name, device_id, red_id, message):
    # 连续按返回键，直到返回首页（假设首页在 3 步内能返回）
    for _ in range(count):
        current_pkg = d.app_current().get('package')
        d.press("back")
        if is_home(d):
            log(f"[{device_name}][{device_id}][{red_id}]等待{config.home_sleep}秒后监控首页未读")
            time.sleep(config.home_sleep)
            if has_red_dot(d):
                log(f"[{device_name}][{device_id}][{red_id}]✅私信任务结束,发现首页有红点消息提示,发出提示音")
                # log(get_ui_full_dump(d, "首页 UI"))
                winsound.Beep(1000, config.un_read_notice)  # 发出声音3秒
            else:
                log(f"[{device_name}][{device_id}][{red_id}]首页无红点(未读消息)")
            break
        if current_pkg.startswith("com.android.launcher") or current_pkg == "com.miui.home":
            break  # 已回到桌面
        time.sleep(config.get_app_other_sleep())

    log(f"[{device_name}][{device_id}][{red_id}]已返回首页,原因:{message}")


def get_ui_full_dump(d, title="页面UI结构") -> str:
    """
    获取当前页面的完整 UI XML 节点信息（所有属性），返回格式化字符串
    """
    try:
        xml = d.dump_hierarchy()
        tree = ET.fromstring(xml)

        lines = [f"\n📋 {title} 全量结构："]
        for node in tree.iter("node"):
            attr_summary = " | ".join(f"{k}: {v}" for k, v in node.attrib.items() if v.strip())
            lines.append(f"🔸 {attr_summary}")

        return "\n".join(lines)

    except Exception as e:
        return f"\n❌ {title} 解析失败：{str(e)}"


# 进入应用后，尝试判断是否是首页
def is_home(d):
    """
    判断是否在首页：比如是否有“发现”和“关注”两个按钮，或者是否出现退出提示等
    """
    d.dump_hierarchy()  # 更新 UI 树
    # 示例条件1：底部导航栏有“发现”和“关注”文本
    if d(text="发现").exists and d(text="关注").exists and d(text="我").exists:
        return True
    return False


# 判断是否有小红点
def has_red_dot(d):
    """
    检测小红书首页是否有红色消息提示圆点
    """
    try:
        xml = d.dump_hierarchy()
        tree = ET.fromstring(xml)

        for node in tree.iter("node"):
            desc = node.attrib.get("content-desc", "")
            clazz = node.attrib.get("class", "")
            if "消息" in desc and "未读" in desc and clazz == "android.view.ViewGroup":
                log(f"✅ 检测到未读消息: {desc}")
                return True
        return False

    except Exception as e:
        log("❌ 检测异常:", e)
        return False


def un_read(device_map):
    """
    在任务未执行期间检查设备的未读消息
    :param device_map: 设备映射
    """
    for device_id, device_info in device_map.items():
        # 检查设备是否在线
        if device_info.get("status") != "online":
            continue

        # 检查任务状态
        if get_task_status(device_id) != "idle":
            log(f"[{device_id}] 任务正在执行，跳过未读检查")
            continue

        device_name = device_info.get("info", {}).get("device_name", device_id)

        # 获取设备连接
        d = get_device_connection(device_id)
        if not d:
            log(f"[{device_name}][{device_id}] 无法连接设备，跳过未读检查")
            continue

        try:
            # # 确保在首页
            # if not is_home(d):
            #     log(f"[{device_name}][{device_id}] 不在首页，尝试返回首页")
            #     back(d, 5, device_name, device_id, "", "未读检查前返回首页")
                
            # 检查是否有未读消息
            if is_home(d):
                log(f"[{device_name}][{device_id}] 等待{config.home_sleep}秒后监控首页未读")
                time.sleep(config.home_sleep)
                if has_red_dot(d):
                    log(f"[{device_name}][{device_id}] ✅发现首页有红点消息提示,发出提示音")
                    winsound.Beep(1000, config.un_read_notice)  # 发出声音3秒
                else:
                    log(f"[{device_name}][{device_id}] 首页无红点(未读消息)")
            else:
                log(f"[{device_name}][{device_id}] 当前不是首页，跳过未读检查")

        except Exception as e:
            log(f"[{device_name}][{device_id}] 未读检查异常: {str(e)}")
        finally:
            # 不立即释放连接，由连接管理机制自动处理
            pass


def send_dm(device_map, red_id):
    d = None
    try:
        device_name = device_map.get("device_name")
        device_id = device_map.get("device_id")
        app = device_map.get("app")
        
        # 设置任务状态为运行中
        set_task_status(device_id, "running")
        
        # 获取设备连接
        d = get_device_connection(device_id)
        if not d:
            log(f"[{device_name}][{device_id}][{red_id}] 无法连接设备")
            return {"status": False, "result": "无法连接设备"}

        log(f"[{device_name}][{device_id}][{red_id}]已连接,打开app主页,等待{config.app_start_sleep}秒,完整信息:{d.device_info}")

        if "1" == app:
            d.app_start("com.xingin.xhs")
        else:
            # 启动应用（弹出选择框）
            d.shell("am start --user 999 -n com.xingin.xhs/.routers.RouterPageActivity")
            time.sleep(config.app_start_sleep);
            # 等待该节点出现（等待5秒）
            # 等待选择框弹出
            if d(resourceId="android.miui:id/app2").wait(timeout=10):
                d(resourceId="android.miui:id/app2").click()
                log(f"[{device_name}][{device_id}][{red_id}]点击了双开小红书")
                # 判断是否是首页，一直撤回到首页，首页有问题发现和关注代表首页，再按一次退出程序也可以作为首页的监控，监控到首页时输出日志
            # else:
            #     log(f"[{device_name}][{device_id}][{red_id}]没找到双开小红书控件")
            #     back(d, 3, device_id, red_id, "没找到双开小红书控件")

        time.sleep(config.app_start_sleep);

        if is_home(d):
            log(f"[{device_name}][{device_id}][{red_id}]当前页面是首页,继续进行")
            log(f"[{device_name}][{device_id}][{red_id}]等待{config.home_sleep}秒后监控首页未读")
            time.sleep(config.home_sleep)
            if has_red_dot(d):
                log(f"[{device_name}][{device_id}][{red_id}]✅私信任务开始,发现首页有红点消息提示,发出提示音")
                # log(get_ui_full_dump(d, "首页 UI"))
                winsound.Beep(1000, config.un_read_notice)  # 发出声音3秒
            else:
                log(f"[{device_name}][{device_id}][{red_id}]首页无红点(未读消息)")
        else:
            back(d, 5, device_name, device_id, red_id, "初始页面不是首页,主动退回,其他正常进行")



        # 点击搜索框
        if d(description="搜索").exists(timeout=5):
            d(description="搜索").click()
        elif d(text="搜索").exists(timeout=5):
            d(text="搜索").click()
        else:
            back(d, 4, device_name, device_id, red_id, "第一步没找到搜索框")
            return {"status": False, "result": "第一步没找到搜索框"}

        time.sleep(config.get_app_other_sleep())
        # print_ui_texts(d, "搜索页 UI")

        d(focused=True).set_text(red_id)
        # d.set_fastinput_ime(True)
        # d.send_keys(red_id)
        # d.set_fastinput_ime(False)

        log(f"[{device_name}][{device_id}][{red_id}]输入小红书id")

        d.press("enter")
        time.sleep(config.get_app_other_sleep())

        # # 点击搜索框
        # if d(description="用户").exists(timeout=5):
        #     d(description="用户").click()
        # elif d(text="用户").exists(timeout=5):
        #     d(text="用户").click()
        # else:
        #     log("------------------------ 未找到用户")
        #     return
        #

        # print_ui_texts(d, "搜索结果页 UI")

        # 点击“用户”Tab
        if d(text="用户").exists:
            d(text="用户").click()
        elif d(text="账号").exists:
            d(text="账号").click()
        elif d(textContains="账号").exists:
            d(textContains="账号").click()
        else:
            back(d, 4, device_name, device_id, red_id, "第二步没找到用户按钮")
            return {"status": False, "result": "第二步没找到用户按钮"}

        time.sleep(config.get_app_other_sleep())
        log(f"[{device_name}][{device_id}][{red_id}]查询用户")

        # 点击“私信”按钮
        if d(descriptionContains="小红书号").exists:
            d(descriptionContains="小红书号").click()
        elif d(textContains="小红书号").exists:
            d(textContains="小红书号").click()
        else:
            back(d, 4, device_name, device_id, red_id, "未找到小红书号按钮(进入个人主页)")
            return {"status": False, "result": "进入个人主页,未找到小红书号按钮"}

        time.sleep(config.get_app_other_sleep())
        log(f"[{device_name}][{device_id}][{red_id}]打开主页")

        if d(descriptionContains="私信").exists:
            d(descriptionContains="私信").click()
        elif d(textContains="私信").exists:
            d(textContains="私信").click()
        else:
            # 等待“发消息”按钮出现并点击
            if d(text="发消息").exists:
                d(text="发消息").click()
            else:
                print("发消息按钮还是未找到")
                text_info = get_ui_full_dump(d, title="主页结构，日志文本给我或者截图，特殊私信按钮")
                log(text_info)
                back(d, 4, device_name, device_id, red_id, "在个人主页未找到私信按钮")
                return {"status": False, "result": "在个人主页未找到私信按钮"}

        time.sleep(config.get_app_other_sleep())
        log(f"[{device_name}][{device_id}][{red_id}]打开私信")

        # 找到输入框，多数是 EditText 类型
        input_box = None
        for i in range(5):  # 多尝试几次确保元素加载
            if d(className="android.widget.EditText").exists:
                input_box = d(className="android.widget.EditText")
                break
            time.sleep(config.get_app_other_sleep())

        if input_box:
            input_box.click()  # 聚焦输入框
            time.sleep(config.get_app_other_sleep())

            mes = config.get_next_message();
            # 切换成 adbkeyboard 输入法，保证 send_keys 生效（需要先初始化）
            # d.set_fastinput_ime(True)
            d.clear_text()  # 清空输入框内容
            # d.send_keys(mes)  # 发送文本
            d(focused=True).set_text(mes)
            # d.set_fastinput_ime(False)  # 恢复默认输入法

            time.sleep(config.get_app_other_sleep())

            # 查询数据库，如果已发送则
            red_id_exists = db_queue.check_red_id_exists(red_id)
            if red_id_exists:
                back(d, 5, device_name, device_id, red_id, "小红书id重复,不需要重复发送(私信界面)")
                return {"status": True, "result": "重复发送"}
            #点击发送按钮或回车
            if d(text="发送").exists:
                d(text="发送").click()
            elif d(description="发送").exists:
                d(description="发送").click()
            else:
                d.press("enter")

            back(d, 5, device_name, device_id, red_id, f"消息发送完成{mes}")
            return {"status": True, "result": f"消息发送完成{mes}"}

        back(d, 5, device_name, device_id, red_id, "在私信页面找不到输入框")
        return {"status": False, "result": "在私信页面找不到输入框"}
    except Exception as e:
        print("捕获到异常:", e)
        error_str = traceback.format_exc()
        back(d, 5, device_name, device_id, red_id, error_str)
        return {"status": False, "result": f"捕获异常:{error_str}"}
    finally:
        # 重置任务状态为空闲
        set_task_status(device_id, "idle")
        
        # 释放设备连接
        release_device_connection(device_id)
